/**
 * Project: apollo-base-tools
 * 
 * File Created at 2017年8月7日
 * 
 * Copyright 2015-2016 dx.com Croporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * DongXue software Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with dx.com.
 */
package org.apollo.sqlite.core;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.log4j.Logger;
import org.apollo.sqlite.utils.KeyValue;
import org.apollo.sqlite.utils.MapperUtil;
import org.apollo.sys.SysLog;

/** 
* @ClassName: SqliteDBExecutor 
* @Description: DB执行器
* @author wuzhenfang(wzfbj2008@163.com)
* @date 2017年8月7日 上午12:27:23 
* @version V1.0 
*/
public class SqliteDBExecutor {

	private final static Logger Log = SysLog.getLogger(SqliteDBExecutor.class);

	private final SQLiteConnectionFactory connectionFactory;

	public SqliteDBExecutor(SQLiteConnectionFactory connectionFactory) {
		this.connectionFactory = connectionFactory;
	}

	/**
	 * 执行SQL，非查询操作
	 * @param sql SQL语句
	 * @throws Exception
	 */
	public void execSQL(final String sql) throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(Statement statement) throws Exception {
				statement.executeUpdate(sql);
			}
		});
	}

	public void dropAllTables() throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(Statement statement) throws Exception {
				helper.deleteAllTables(db);
			}
		});
	}

	public List<String> queryAllTableNames(String sql) {
		List<String> tableNames = new ArrayList<>();
		SQLiteDatabase db = helper.getReadableDatabase();
		Cursor cursor = db.rawQuery(sql, null);
		if (cursor != null) {
			try {
				while (cursor.moveToNext()) {
					tableNames.add(cursor.getString(0));
				}
			} finally {
				cursor.close();
			}
		}
		return tableNames;
	}

	public List<ColumnInfo> queryAllColumnsFromTable(String sql) throws NoSuchFieldException, InstantiationException, ParseException, IllegalAccessException {
		List<ColumnInfo> columnInfos = new ArrayList<>();
		SQLiteDatabase db = helper.getReadableDatabase();
		Cursor cursor = db.rawQuery(sql, null);
		if (cursor != null) {
			try {
				while (cursor.moveToNext()) {
					ColumnInfo columnInfo = ObjectUtil.buildObject(ColumnInfo.class, cursor);
					columnInfos.add(columnInfo);
				}
			} finally {
				cursor.close();
			}
		}
		return columnInfos;
	}

	public boolean isTableExist(String sql) {
		SQLiteDatabase db = helper.getReadableDatabase();
		Cursor cursor = db.rawQuery(sql, null);
		if (cursor != null) {
			try {
				if (cursor.moveToNext()) {
					if (cursor.getInt(0) != 0) {
						return true;
					}
				}
			} finally {
				cursor.close();
			}
		}
		return false;
	}

	public void insert(final Object object) throws Exception {
		final String table = TableUtil.getTableName(object);
		final ContentValues values = FieldUtil.getContentValues(object);
		// 插入表
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(SQLiteDatabase db) throws Exception {
				db.insert(table, null, values);
			}
		});
	}

	public void insert(List objectList) throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(SQLiteDatabase db) throws Exception {
				for (Object object : objectList) {
					final String table = TableUtil.getTableName(object);
					final ContentValues values = FieldUtil.getContentValues(object);
					db.insert(table, null, values);
				}
			}
		});
	}

	public void update(Object object) throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(SQLiteDatabase db) throws Exception {
				// 根据id更新object
				KeyValue idColumn = FieldUtil.getIDColumn(object);
				if (null == idColumn) {
					throw new IllegalStateException("Object does not include the id field");
				}
				if (null == idColumn.getValue()) {
					throw new IllegalStateException("The value of the id field cannot be null");
				}
				String tableName = TableUtil.getTableName(object);
				ContentValues values = FieldUtil.getContentValuesWithOutID(object);
				String whereClause = idColumn.getKey() + "=?";
				String[] whereArgs = new String[] { idColumn.getValue().toString() };

				db.update(tableName, values, whereClause, whereArgs);
			}
		});
	}

	public void delete(Object object) throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(Statement statement) throws Exception {
				KeyValue column = MapperUtil.getIDColumn(object);
				if (null == column) {
					throw new IllegalStateException("Object does not include the id field");
				}
				if (null == column.getValue()) {
					throw new IllegalStateException("The value of the id field cannot be null");
				}

				String tableName = MapperUtil.getTableName(object);
				String whereClause = column.getKey() + "=?";
				String[] whereArgs = new String[] { column.getValue().toString() };
				// 根据id删除object
				statement.delete(tableName, whereClause, whereArgs);
			}
		});
	}

	public void delete(List objectList) throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(SQLiteDatabase db) throws Exception {
				for (Object object : objectList) {
					KeyValue column = MapperUtil.getIDColumn(object);
					if (null == column) {
						throw new IllegalStateException("Object does not include the id field");
					}
					if (null == column.getValue()) {
						throw new IllegalStateException("The value of the id field cannot be null");
					}
					String tableName = MapperUtil.getTableName(object);
					String whereClause = column.getKey() + "=?";
					String[] whereArgs = new String[] { column.getValue().toString() };
					// 根据id删除object
					db.delete(tableName, whereClause, whereArgs);
				}
			}
		});
	}

	public void delete(final Class<?> clazz, final String whereClause, final String[] whereArgs) throws Exception {
		executeTransaction(new NoQueryOperation() {
			@Override
			public void onOperation(SQLiteDatabase db) {
				db.delete(MapperUtil.getTableName(clazz), whereClause, whereArgs);
			}
		});
	}

	public <T> SelectBuilder<T> query(Class<T> clazz) {
		return new SelectBuilder<>(this, clazz);
	}

	public <T> Cursor query(SelectBuilder<T> builder) {
		SQLiteDatabase db = helper.getReadableDatabase();
		return db.query(MapperUtil.getTableName(builder.objectClass), builder.columns, builder.whereSection, builder.whereArgs, builder.groupBy, builder.having, builder.orderBy,
				builder.limit);
	}

	public Cursor rawQuery(String sql, String[] selectionArgs) {
		SQLiteDatabase db = helper.getReadableDatabase();
		return db.rawQuery(sql, null);
	}

	/**
	 * 执行非查询操作事物
	 * @param operation 非查询操作
	 */
	public void executeTransaction(NoQueryOperation operation) throws Exception {
		Connection connection = getConnection();
		try {
			connection.setAutoCommit(false);
			Log.debug("NoQuery beginTransaction");
			if (operation != null) {
				operation.onOperation(connection.createStatement());
			}
			connection.commit();
			Log.debug("NoQuery transactionSuccessful");
		} catch (Exception e) {
			connection.rollback();
			Log.error("EndTransaction rollback", e);
		} finally {
			close();
			Log.debug("NoQuery endTransaction");
		}
	}

	public interface NoQueryOperation {
		void onOperation(Statement statement) throws Exception;
	}
	
	private Connection getConnection() {
		try {
			return connectionFactory.getConnection();
		} catch (Exception e) {
			Log.error("获取connection失败", e);
		}
		return null;
	}
	
	private Statement getStatement() {
		try {
			return connectionFactory.getStatement();
		} catch (SQLException e) {
			Log.error("获取statement失败", e);
		}
		return null;
	}
	
	private void close() {
		connectionFactory.close();
	}
}
